﻿#region Using directives

using System;
using System.Windows.Interop;
using System.Windows.Input;
using System.Runtime.InteropServices;
using System.Windows.Controls;
using System.Windows;

#endregion

namespace Wintellect.LiveFx.Cloudboard
{
  public struct HotkeyCombination
  {
    public Key Key;
    public HotkeyModifiers Modifiers;
    public HotkeyCombination(Key key, HotkeyModifiers modifiers)
    {
      this.Key = key;
      this.Modifiers = modifiers;
    }

    public override bool Equals(object obj)
    {
      if (obj is HotkeyCombination)
      {
        HotkeyCombination b = (HotkeyCombination)obj;
        if (b.Key == this.Key && b.Modifiers == this.Modifiers) return true;
      }
      return false;
    }

    public override int GetHashCode()
    {
      return (int)this.Modifiers << 16 + (int)this.Key;
    }

    public static bool operator ==(HotkeyCombination a, HotkeyCombination b)
    {
      return a.Equals(b);
    }

    public static bool operator !=(HotkeyCombination a, HotkeyCombination b)
    {
      return !a.Equals(b);
    }

    internal static HotkeyCombination FromIntPtr(IntPtr wParam)
    {
      return new HotkeyCombination(
        ((KeyCode)(((Int32)wParam) & 0xff)).ToInputKey(), 
        (HotkeyModifiers)((Int32)(wParam) >> 8));
    }

    internal IntPtr ToIntPtr()
    {
      return (IntPtr)((((Int32)Modifiers) << 8) | ((Int32)Key.ToKeyCode()));
    }

    internal static HotkeyCombination FromHotkey(Hotkey value)
    {
      HotkeyCombination output = new HotkeyCombination(
        value.Key,
        (value.Alt ? HotkeyModifiers.Alt : HotkeyModifiers.None) |
        (value.Control ? HotkeyModifiers.Control : HotkeyModifiers.None) |
        (value.Shift ? HotkeyModifiers.Shift : HotkeyModifiers.None));
      return output;
    }

    internal Hotkey ToHotkey()
    {
      Hotkey output = new Hotkey()
      {
        Key = this.Key,
        Alt = (this.Modifiers & HotkeyModifiers.Alt) == HotkeyModifiers.Alt,
        Control = (this.Modifiers & HotkeyModifiers.Control) == HotkeyModifiers.Control,
        Shift = (this.Modifiers & HotkeyModifiers.Shift) == HotkeyModifiers.Shift
      };
      return output;
    }

    public static implicit operator IntPtr(HotkeyCombination value)
    {
      return value.ToIntPtr();
    }

    public static implicit operator HotkeyCombination(IntPtr value)
    {
      return HotkeyCombination.FromIntPtr(value);
    }

    public static implicit operator HotkeyCombination(Hotkey value)
    {
      return HotkeyCombination.FromHotkey(value);
    }

    public static implicit operator Hotkey(HotkeyCombination value)
    {
      return value.ToHotkey();
    }    
  }

  [Flags]
  public enum HotkeyModifiers
  {
    None = 0,
    Shift = 0x01,
    Control = 0x02,
    Alt = 0x04,
    Extended = 0x08
  }

#if false
   [Flags]
   public enum HotkeyCombineRuleFlags {
      None = 0x0001,
      S = 0x0002,
      C = 0x0004,
      A = 0x0008,
      SC = 0x0010,
      SA = 0x0020,
      CA = 0x0040,
      SCA = 0x0080
   }
#endif

  public sealed class HotkeyControl : HwndHost
  {
    private const String HOTKEY_CLASS = "msctls_hotkey32";
    private const int HOST_ID = 0x00000002;
    private const int HOTKEY_ID = 0x00000001;

    private IntPtr m_hwndControl;
    private IntPtr hFont;
    private HotkeyCombination hotkeyCombination;

    public HotkeyControl()
    {
    }

    // http://msdn.microsoft.com/en-us/library/bb775237(VS.85).aspx
    //public void SetRules(HotKeyFlag flags, Key key) {
    //   SendMessage(m_hwndControl, HKM_SETHOTKEY, (IntPtr)((((Int32)flags) << 8) | ((Int32)key)), IntPtr.Zero);
    //}


    public HotkeyCombination Hotkey
    {
      set 
      {
        if (this.hotkeyCombination != value)
        {
          this.hotkeyCombination = value;
          if (this.m_hwndControl != IntPtr.Zero)
          {
            NativeMethods.SetHotkey(m_hwndControl, value);
          }
        }
      }
      get 
      {
        if (this.m_hwndControl != IntPtr.Zero)
        {
          this.hotkeyCombination = NativeMethods.GetHotkey(m_hwndControl); 
        }
        return this.hotkeyCombination;
      }
    }

    protected override void OnVisualParentChanged(System.Windows.DependencyObject oldParent)
    {
      base.OnVisualParentChanged(oldParent);
      for (DependencyObject parent = this.Parent;
        parent != null;
        )
      {
        Control hostingControl = parent as Control;
        if (hostingControl != null)
        {
          this.hFont = NativeMethods.CreateFont(
            -1 * (int)hostingControl.FontSize,
            0,
            0,
            0,
            hostingControl.FontWeight.ToOpenTypeWeight(),
            hostingControl.FontStyle == FontStyles.Italic,
            false,
            false,
            1,
            0,
            0,
            0,
            0,
            hostingControl.FontFamily.Source);
          break;
        }

        FrameworkElement logicalParent = parent as FrameworkElement;
        parent = logicalParent.Parent; 
      }
      
    }

    protected override HandleRef BuildWindowCore(HandleRef hwndParent)
    {
      m_hwndControl = NativeMethods.CreateWindowEx(
        0, 
        HOTKEY_CLASS, 
        "",
        WindowStyle.WS_CHILD | WindowStyle.WS_VISIBLE | WindowStyle.WS_BORDER, 
        0, 
        0, 
        0, 
        0, 
        hwndParent.Handle, 
        (IntPtr)HOTKEY_ID, 
        IntPtr.Zero, 
        0);
      NativeMethods.SetFont(this.m_hwndControl, this.hFont);
      NativeMethods.SetHotkey(this.m_hwndControl, this.hotkeyCombination);
      return new HandleRef(this, m_hwndControl);
    }

    protected override void DestroyWindowCore(HandleRef hwnd)
    {
      NativeMethods.DestroyWindow(hwnd.Handle);
    }

    protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
      return base.WndProc(hwnd, msg, wParam, lParam, ref handled);
    }
  }
}
